<template>
  <div class="td">
    <div class="td-box" ref="can"></div>
    <div class="tool">
      <el-button size="small" class="btn" @click="toFace('up')">顶面</el-button>
      <el-button size="small" class="btn" @click="toFace('down')"
        >底面</el-button
      >
      <el-button size="small" class="btn" @click="toFace('left')"
        >左面</el-button
      >
      <el-button size="small" class="btn" @click="toFace('right')"
        >右面</el-button
      >
      <el-button size="small" class="btn" @click="toFace('front')"
        >前面</el-button
      >
      <el-button size="small" class="btn" @click="toFace('back')"
        >后面</el-button
      >
    </div>
  </div>
</template>
<script setup>
import { onMounted, ref, onBeforeUnmount, computed, watch } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import editStore from "@/store/editStore";
import { getAssetsFile } from "@/utils/index.js";
import { debounce } from "@/utils";

let props = defineProps({
  from: {
    type: String,
    default: "",
  },
  storeJson: {
    type: Object,
    default: () => {},
  },
});

let frameId = null; //旋转动作id

const store = computed(() => {
  return props.from == "home" ? props.storeJson : editStore();
});

let can = ref();
let render = null;
let controls = null;
let camera = null;
let mesh = null;
let lineMesh = null;
let innerBox = null;
let currentInnerBox = null;
let group = new THREE.Group();

let scene = new THREE.Scene();

let loader = new THREE.TextureLoader();
loader.setCrossOrigin("Anonymous");

let AmbientLight = new THREE.AmbientLight("#fff", 1);
scene.add(AmbientLight);

function init() {
  innerBox = store.value.innerBox;
  currentInnerBox = store.value.innerBox.boxList[store.value.reelIndex];

  let geometry = new THREE.BoxGeometry(
    innerBox.boxLength,
    innerBox.boxHeigh,
    innerBox.boxWidth
  );

  let Material1 = new THREE.MeshBasicMaterial({
    color: "#966f33",
  });

  mesh = new THREE.Mesh(geometry, Material1);
  mesh.position.set(0, 0, 0);

  lineMesh = initLineMesh(geometry, { color: "#111" }); //#901d00

  group.add(mesh);
  group.add(lineMesh);
  scene.add(group);

  let float = 2; //防止重影距离

  currentInnerBox.leftComponentList.forEach((item) => {
    if (item.type == "Labels") {
      let x = -innerBox.boxLength / 2 - float;
      let y = item.x - innerBox.boxHeigh / 2 + item.length / 2;
      let z = item.y - innerBox.boxWidth / 2 + item.width / 2;
      let rx = ((item.angle + 90) / 180) * Math.PI;
      let ry = Math.PI / 2;
      let rz = 0;
      let uv = [0, 0, 1, 0, 0, 1, 1, 1];
      createPlaneGeometry(item, { x, y, z }, { rx, ry, rz }, uv);
    }
  });
  currentInnerBox.downComponentList.forEach((item) => {
    if (item.type == "Labels") {
      let x = item.x - innerBox.boxLength / 2 + item.length / 2;
      let y = -innerBox.boxHeigh / 2 - float;
      let z = item.y - innerBox.boxWidth / 2 + item.width / 2;
      let rx = -Math.PI / 2;
      let ry = 0;
      let rz = -(item.angle / 180) * Math.PI;
      let uv = [1, 1, 0, 1, 1, 0, 0, 0];
      createPlaneGeometry(item, { x, y, z }, { rx, ry, rz }, uv);
    }
  });
  currentInnerBox.rightComponentList.forEach((item) => {
    if (item.type == "Labels") {
      let x = innerBox.boxLength / 2 + float;
      let y = -item.x + innerBox.boxHeigh / 2 - item.length / 2;
      let z = item.y - innerBox.boxWidth / 2 + item.width / 2;
      let rx = -((item.angle + 90) / 180) * Math.PI;
      let ry = -Math.PI / 2;
      let rz = 0;
      let uv = [1, 1, 0, 1, 1, 0, 0, 0];
      createPlaneGeometry(item, { x, y, z }, { rx, ry, rz }, uv);
    }
  });
  currentInnerBox.upComponentList.forEach((item) => {
    if (item.type == "Labels") {
      let x = item.x - innerBox.boxLength / 2 + item.length / 2;
      let y = innerBox.boxHeigh / 2 + float;
      let z = item.y - innerBox.boxWidth / 2 + item.width / 2;
      let rx = -Math.PI / 2;
      let ry = 0;
      let rz = -(item.angle / 180) * Math.PI;

      createPlaneGeometry(item, { x, y, z }, { rx, ry, rz });
    }
  });
  currentInnerBox.frontComponentList.forEach((item) => {
    if (item.type == "Labels") {
      let x = item.x - innerBox.boxLength / 2 + item.length / 2;
      let y = -item.y + innerBox.boxHeigh / 2 - item.width / 2;
      let z = innerBox.boxWidth / 2 + float;
      let rx = 0;
      let ry = 0;
      let rz = -(item.angle / 180) * Math.PI;
      createPlaneGeometry(item, { x, y, z }, { rx, ry, rz });
    }
  });
  currentInnerBox.backComponentList.forEach((item) => {
    if (item.type == "Labels") {
      let x = item.x - innerBox.boxLength / 2 + item.length / 2;
      let y = -item.y + innerBox.boxHeigh / 2 - item.width / 2;
      let z = -innerBox.boxWidth / 2 - float;
      let rx = 0;
      let ry = 0;
      let rz = -(item.angle / 180) * Math.PI;
      let uv = [1, 1, 0, 1, 1, 0, 0, 0];
      createPlaneGeometry(item, { x, y, z }, { rx, ry, rz }, uv);
    }
  });
}

init();

function initLineMesh(geometry, mopt = { color: 0xff0000 }) {
  let edges = new THREE.EdgesGeometry(geometry);
  let lineMesh = new THREE.LineSegments(
    edges,
    new THREE.LineBasicMaterial(mopt)
  );
  return lineMesh;
}

function createPlaneGeometry(
  item,
  position,
  rotation,
  uv = [0, 1, 1, 1, 0, 0, 1, 0]
) {
  let label = new THREE.PlaneGeometry(item.length, item.width);
  let texture = loader.load(item.picName, () => {
    render.render(scene, camera);
  });
  let material = new THREE.MeshBasicMaterial({
    map: texture,
    side: THREE.DoubleSide,
  });
  let lmesh = new THREE.Mesh(label, material);

  lmesh.position.set(position.x, position.y, position.z);
  lmesh.rotation.set(rotation.rx, rotation.ry, rotation.rz);
  lmesh.geometry.setAttribute(
    "uv",
    new THREE.BufferAttribute(new Float32Array(uv), 2)
  );
  mesh.add(lmesh);
  if (item.qaStamp) {
    createCircleGeometry(item, lmesh);
  }
}

function createCircleGeometry(item, label) {
  let r = 10;
  let qa = new THREE.CircleGeometry(r);
  let texture = loader.load(getAssetsFile("QA"));
  let material = new THREE.MeshBasicMaterial({
    map: texture,
    side: THREE.DoubleSide,
  });
  let lmesh = new THREE.Mesh(qa, material);
  // lmesh.position.set(item.qaStampx + r, item.qaStampy - item.width + r, 1);
  lmesh.position.set(
    item.qaStampX - item.length / 2 + r,
    -item.qaStampY + item.width / 2 - r,
    1
  );
  lmesh.rotation.set(0, 0, (item.qaStampAngle / 180) * Math.PI);
  label.add(lmesh);
}

function action() {
  group.rotation.x += 0.001;
  group.rotation.y += 0.001;
  group.rotation.z += 0.001;
  render.render(scene, camera);
  frameId = requestAnimationFrame(action);
}

function resize() {
  render.setSize(can.value.offsetWidth, can.value.offsetHeight); // 根据缩放的窗口重新设置画布的大小
  // 垂直相机宽高比
  camera.aspect = can.value.offsetWidth / can.value.offsetHeight;
  // 更新相机投影矩阵
  camera.updateProjectionMatrix();
}
let debResize = debounce(resize, 100);

function toFace(f) {
  let len = innerBox.boxLength * 3;
  if (f == "up") {
    camera.position.set(0, len + 5, 0);
    group.rotation.set(0, 0, 0);
  } else if (f == "down") {
    camera.position.set(0, -len - 5, 0);
    group.rotation.set(0, Math.PI, 0);
  } else if (f == "left") {
    camera.position.set(-len - 5, 0, 0);
  } else if (f == "right") {
    camera.position.set(len + 5, 0, 0);
  } else if (f == "front") {
    camera.position.set(0, 0, len + 5);
  } else if (f == "back") {
    camera.position.set(0, 0, -len - 5);
  }
  camera.lookAt(0, 0, 0); //重新计算相机视线方向
  render.render(scene, camera);
}

onMounted(() => {
  camera = new THREE.PerspectiveCamera(
    30,
    can.value.offsetWidth / can.value.offsetHeight,
    1,
    5000
  );
  camera.lookAt(0, 0, 0);
  camera.position.set(0, innerBox.boxLength * 4, 0);
  render = new THREE.WebGLRenderer({
    alpha: true,
    antialias: true,
  });
  render.setSize(can.value.offsetWidth, can.value.offsetHeight);
  render.setPixelRatio(window.devicePixelRatio);

  let controls = new OrbitControls(camera, render.domElement);
  controls.addEventListener("change", () => {
    render.render(scene, camera);
  });
  can.value.appendChild(render.domElement);
  if (props.from == "home") {
    action();
  } else {
    render.render(scene, camera);
  }
  window.addEventListener("resize", debResize);
});

onBeforeUnmount(() => {
  if (props.from == "home") {
    frameId && cancelAnimationFrame(frameId);
  }
  window.removeEventListener("resize", debResize);

  render.dispose();
  controls = null;
  render = null;
  camera = null;
  scene = null;
  group = null;
});

watch(
  () => props.storeJson,
  () => {
    group.remove(mesh);
    // scene.remove(lineMesh);
    mesh.geometry.dispose();
    init();
    render.render(scene, camera);
  }
);
</script>
<style scoped lang="scss">
.td {
  position: relative;
  width: 100%;
  height: 100%;
  .td-box {
    width: 100%;
    height: 100%;
  }
  .tool {
    position: absolute;
    top: 50px;
    right: 10px;
    z-index: 999;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    .btn {
      margin: 0;
      margin-bottom: 8px;
    }
  }
}
</style>
